/*
    游戏房间管理模块：
        1.对匹配成功的玩家创建房间，建立起一个小范围的玩家之间的关联关系，房间里一个玩家产生的动作会广播给房间里的其他用户
        2.房间可能有很多，有的刚刚创建，有的正在比赛，有的比赛已经结束要释放房间，所以还要对房间进行管理
    游戏房间的设计：
        管理的数据：
            1.房间id
            2.房间的状态——决定了一个玩家退出房间时所做的动作，如果房间刚创建就退出，则另一方不战而胜，如果比赛已经结束了，此时退出房间，不影响结果
            3.房间中玩家的数量——决定了房间什么时候销毁
            4.白棋玩家id
            5.黑棋玩家id
            6.在线用户信息表的句柄——游戏胜利或者失败更新数据
            7.棋盘信息
            8.在线用户管理句柄
        提供的操作： 不管是哪个动作，只要是合理的，就要广播给房间里的其他用户
            1.下棋
            2.聊天
    房间管理类的设计：
        管理的数据：
            1.数据管理模块句柄
            2.在线用户管理模块句柄
            3.房间ID分配计数器
            4.互斥锁
            5.unordered_map<room_id, std::shared_ptr<room>> 房间信息管理
            6.unordered_map<id, room_id> 房间id与用户id的关联关系管理
        提供的操作
            1.创建房间（需要两个玩家的用户id）
            2.查找房间（通过房间id或者用户id）
            3.销毁房间（根据房间id销毁房间/房间没有玩家了，销毁房间）
*/
#pragma once

#include "util.hpp"
#include "db.hpp"
#include "online.hpp"

#define BOARD_ROW 15
#define BOARD_COL 15
#define WHITE_CHESS 1
#define BLACK_CHESS 2
typedef enum {
    START,
    OVER
} room_state;

class room
{
public:
    room(int room_id, User_Info_Table *tb_handle, online_user_manager *online_user)
        :_room_id(room_id), 
         _table_handle(tb_handle),
         _online_user(online_user),
         _state(START),
         _player_count(0),
         _board(BOARD_ROW, std::vector<int>(BOARD_COL))
    {
        debug("%d 房间创建成功!!!", _room_id);
    }
    ~room() 
    {
        debug("%d 房间被销毁!!!", _room_id);
    }
    int room_id() { return _room_id; }
    room_state state() { return _state; }
    int player_count() { return _player_count; }
    int white_id() { return _white_id; }
    int black_id() { return _black_id; }
    void add_white_chess(int id) { _white_id = id; _player_count++; }
    void add_black_chess(int id) { _black_id = id; _player_count++; }

    // 下棋操作
    // 玩家执行下棋操作，我们所收到的请求报文格式{ "optype" = "put_chess", "room_id" = 222, "id" = 1, "row" = 3, "col" = 1}
    // 响应报文格式：
    // 失败-> {"optype" = "put_chess", "result" = "false", "reason" = "xxxx"}
    // 成功-> {"optype" = "put_chess", "result" = "true", "reason" = "xxx", "id" = 1, "row" = 3, "col" = 1, winner = 0/id}
    Json::Value handle_chess(Json::Value &req)
    {
        Json::Value resp = req;
        // 1. 判断房间号是否匹配 —— 外部
        // 2.判断房间中的两位玩家是否在线，不在线，则另一方胜利，掉线者扣分，如果都掉线了，则都扣分
        int row = req["row"].asInt();
        int col = req["col"].asInt();
        if(!_online_user->is_in_game_room(_white_id) || !_online_user->is_in_game_room(_black_id)) {// 至少有一方掉线
            if(!_online_user->is_in_game_room(_white_id) && !_online_user->is_in_game_room(_black_id)) {// 同时掉线
                resp["result"] = false;
                resp["reason"] = "双方都掉线了";
                resp["winner"] = 0;
            }
            else if(!_online_user->is_in_game_room(_white_id)) {// 白棋掉线
                resp["result"] = true;
                resp["reason"] = "对方掉线";
                resp["winner"] = _black_id;
            }
            else {// 黑棋掉线
                resp["result"] = true;
                resp["reason"] = "对方掉线";
                resp["winner"] = _white_id;
            }
            return resp;
        }
        // 3. 获取下棋位置，判断该位置是否合理（是否被占用）
        if(_board[row][col] != 0) {// 该位置已经有棋子了
            resp["result"] = false;
            resp["reason"] = "该位置已经有棋子了";
            return resp;
        }
        int color = (req["uid"].asInt() == _white_id ? WHITE_CHESS : BLACK_CHESS);
        _board[row][col] = color;//下棋
        // 4.判断是否有玩家胜利，四个方向，判断是否有5个及以上棋子的
        int winner_id = check(row, col, color);
        resp["result"] = true;
        resp["reason"] = "成五";
        resp["winner"] = winner_id;
        return resp;
    }

    // 聊天请求报文 {"optype" = "chat", room_id = 1, "id" = 1, "message" = "科利马查"}
    Json::Value handle_chat(Json::Value &req)
    {
        Json::Value resp = req;
        // 1. 判断房间号是否匹配 —— 外部
        // 2.检查消息中是否包含敏感词，如包含敏感词用*代替
        std::string msg = req["message"].asString();
        std::string shield = "垃圾";
        size_t pos = msg.find(shield);
        if(pos != std::string::npos) {
            int i = shield.size();
            while(i--) {
                msg[pos++] = '*';
            }
            resp["message"] = msg;
        }
        // 3.广播消息——返回消息
        Json::Value sender_info;
        _table_handle->select_by_id(req["uid"].asInt(), sender_info);
        resp["sender_username"] = sender_info["username"].asString();
        resp["result"] = true;

        return resp;
    }
    void handle_exit(int id)
    {
        Json::Value resp;
        // 玩家退出房间，判断是游戏中退出，还是游戏结束退出
        if(_state == START) {// 游戏中退出
            int winner_id = id == _white_id ? _black_id : _white_id;
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["reason"] = "对方掉线";
            resp["uid"] = id;
            resp["row"] = -1;
            resp["col"] = -1;
            resp["winner"] = winner_id;
            int loser_id = winner_id == _white_id ? _black_id : _white_id;
            _table_handle->win(winner_id);
            _table_handle->lose(loser_id);
            _state = OVER;
            broadcast(resp);
        }
        _player_count--;
    }
    void handle_request(Json::Value &req)
    {
        Json::Value resp;
        // 1. 判断房间号是否匹配
        if(req["room_id"].asInt() != _room_id) {// 房间号不匹配，直接构建响应报文，返回下棋失败
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "房间号不匹配";
            return broadcast(resp);
        }
        // 2.分类请求类型
        if(req["optype"].asString() == "put_chess") {
            resp = handle_chess(req);
            if(resp["winner"].asInt() != 0) {// 有人获胜
                // 调用数据库接口，修改信息
                int winner_id = resp["winner"].asInt();
                int loser_id = resp["winner"].asInt() == _white_id ? _black_id : _white_id;
                _table_handle->win(winner_id);
                _table_handle->lose(loser_id);
                _state = OVER;
            }
        }
        else if(req["optype"].asString() == "chat") {
            resp = handle_chat(req);
        }
        else {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "Unknow request";
        }
        return broadcast(resp);
    }
    // 广播
    void broadcast(Json::Value &resp)
    {
        // 将格式化信息进行序列化成为一个字符串
        std::string str;
        json_util::serialize(resp, str);
        // 获取房间内用户的连接，并发送响应信息
        ws_server_t::connection_ptr white_conn = _online_user->get_conn_from_room(_white_id);
        if(white_conn.get() != nullptr) {
            white_conn->send(str);
        }
        ws_server_t::connection_ptr black_conn = _online_user->get_conn_from_room(_black_id);
        if(black_conn.get() != nullptr) {
            black_conn->send(str);
        }
    }
private:
    bool is_five(int row, int col, int row_offset, int col_offset, int color) 
    {
        int count = 1;
        int search_row = row + row_offset;
        int search_col = col + col_offset;
        while(search_row>=0 && search_row<BOARD_ROW &&
            search_col>=0 && search_col<BOARD_COL &&
            _board[search_row][search_col] == color) {
                count++;
                search_row += row_offset;
                search_col += col_offset;
        }
        // 反方向继续判断
        search_row = row - row_offset;
        search_col = col - col_offset;
        while(search_row>=0 && search_row<BOARD_ROW &&
            search_col>=0 && search_col<BOARD_COL &&
            _board[search_row][search_col] == color) {
                count++;
                search_row -= row_offset;
                search_col -= col_offset;
        }
        return (count >= 5);
    }
    int check(int row, int col, int color)
    {// 0表示无人获胜，非0即id，表示获胜者的id
        if(is_five(row, col, 0, 1, color)/*横*/ ||
        is_five(row, col, 1, 0, color)/*纵*/ ||
        is_five(row, col, -1, 1, color)/*左下到右上*/ ||
        is_five(row, col, 1, 1, color)/*左上到右下*/) {
            return color == WHITE_CHESS ? _white_id : _black_id;
        }
        return 0;
    }
private:
    int _room_id; //房间id
    room_state _state; // 房间状态
    int _player_count; // 玩家数量
    int _white_id; // 白棋玩家id
    int _black_id; // 黑棋玩家id
    User_Info_Table *_table_handle; // 数据库句柄
    online_user_manager *_online_user; // 在线玩家
    std::vector<std::vector<int>> _board; //棋盘
};


class room_manager
{
public:
    room_manager(User_Info_Table *tb, online_user_manager *ou)
        :_room_counter(1), 
         _table_handle(tb),
         _online_user(ou)
    {}
    ~room_manager(){}
    std::shared_ptr<room> create_room(int id1, int id2)
    {
        // 1.两个用户都在大厅中
        if(!_online_user->is_in_game_hall(id1) || !_online_user->is_in_game_hall(id2)) {
            return std::shared_ptr<room>();
        }
        // 2.创建房间
        std::unique_lock<std::mutex> lock(_mutex);
        std::shared_ptr<room> r = std::make_shared<room>(_room_counter, _table_handle, _online_user);
        // 3.分配黑白棋 
        r->add_black_chess(id1);
        r->add_white_chess(id2);
        // 4.建立映射关系
        _rooms.emplace(_room_counter, r);
        _user_2_room.emplace(id1, _room_counter);
        _user_2_room.emplace(id2, _room_counter);
        // 5.自增计数器
        _room_counter++;
        return r;
    }
    std::shared_ptr<room> get_room_by_room_id(int room_id)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto iter = _rooms.find(room_id);
        if(iter == _rooms.end()) {
            return std::shared_ptr<room>();
        }
        return iter->second;
    }
    std::shared_ptr<room> get_room_by_user_id(int user_id)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto iter1 = _user_2_room.find(user_id);
        if(iter1 == _user_2_room.end()) {
            return std::shared_ptr<room>();
        }
        int room_id = iter1->second;
        auto iter2 = _rooms.find(room_id);
        if(iter2 == _rooms.end()) {
            return std::shared_ptr<room>();
        }
        return iter2->second;
    }
    void remove_room(int room_id)
    {
        auto room = get_room_by_room_id(room_id);
        if(room.get() == nullptr) return;
        int uid1 = room->black_id();
        int uid2 = room->white_id();
        
        std::unique_lock<std::mutex> lock(_mutex);
        _user_2_room.erase(uid1);
        _user_2_room.erase(uid2);
        _rooms.erase(room_id);
    }
    void remove_room_user(int user_id)
    {
        auto room = get_room_by_user_id(user_id);
        if(room.get() == nullptr) return;
        room->handle_exit(user_id);
        if(room->player_count() == 0) {
            remove_room(room->room_id());
        }
    }
    void player_enter(int uid)
    {
        _table_handle->enter_game(uid); // 更新用户状态为游戏中
        notify_friends_status_change(uid, "游戏中");
    }

    void player_exit(int uid)
    {
        _table_handle->exit_game(uid); // 更新用户状态为在线
        notify_friends_status_change(uid, "在线");
    }

    void notify_friends_status_change(int uid, const std::string& status)
    {
        std::vector<int> friend_ids;
        _table_handle->select_friend_id_by_id(uid, friend_ids);

        Json::Value resp;
        resp["optype"] = "friend_status_change";
        resp["data"]["id"] = uid;
        resp["data"]["status"] = status;
        std::string json_str;
        json_util::serialize(resp, json_str);

        for (int friend_id : friend_ids)
        {
            auto conn = _online_user->get_conn_from_hall(friend_id);
            if (conn.get())
            {
                conn->send(json_str);
            }
        }
    }
private:
    int _room_counter;// 房间计数器
    std::mutex _mutex;
    User_Info_Table *_table_handle; // 数据库句柄
    online_user_manager *_online_user; // 在线玩家句柄
    std::unordered_map<int, std::shared_ptr<room>> _rooms;
    std::unordered_map<int, int> _user_2_room;
};